#include "socket.hpp"
#include <pthread.h>

#include <vector>
#include <unordered_map>
#include <sstream>
#include <fstream>

using namespace std;

const string wwwroot = "./wwwroot";
const string home_page = "index.html";
const string err_file = "./wwwroot/err.html";
const string sep = "\r\n";

class service;

struct thread_date {
    thread_date(int fd, service *svr) :connect_fd(fd), server(svr) {}

    int connect_fd;
    service *server;
};

void print_seps(int length) {
    for(int i = 0; i < length; ++i) {
        printf("-");
    }
    printf("\n");
}

// 定制协议
class request {
public: 
    // 反序列
    void deserializer(const string &content) {
        size_t idx = 0;
        while(true) {
            size_t pos = content.find(sep, idx);
            if(pos == string::npos || pos == idx) {
                break;
            }
            _headers.push_back(content.substr(idx, pos - idx + 1));
            idx = pos + sep.size();
        }
        _text = content.substr(idx);
        print_seps(100);
        cout << "content: " << endl;
        for(auto &s : _headers) {
            cout << s << endl;
        }
        cout << "text: " << endl; 
        cout << _text << endl;
        print_seps(100);
    }

    void get_request_line() {
        stringstream s(_headers.front());
        s >> _method >> _url >> _version;
    }

    string get_path() {
        string file_path = wwwroot;
        if(_url == "/" || _url == home_page) {
            file_path += "/";
            file_path += home_page;
        }
        else {
            file_path += _url;
        }
        size_t pos = file_path.find_last_of('.');
        if(pos == string::npos) {
            _suffix = ".html";
        }
        else {
            _suffix = file_path.substr(pos);
        }
        return file_path;
    }

public:
    vector<string> _headers;
    string _text;
    string _method;
    string _url;
    string _version;
    string _suffix;
};


class service {
    static const size_t default_buffer_size = 4096 * 3;
public:
    service(uint16_t port) :_port(port) {}

    static string read_html_file(const string &file) {
        ifstream in(file.c_str(), ios::binary);
        if(!in.is_open()) {
            return "";
        }
        in.seekg(0, std::ios_base::end);
        auto len = in.tellg();
        in.seekg(0, std::ios_base::beg);

        std::string content;
        content.resize(len);
        in.read((char*)content.c_str(), content.size());
        // string s;
        // string content;
        // while(getline(ifs, s)) {
        //     content += s;
        // }
        // content += sep;
        in.close();
        return content;
    }

    static void *thread_task(void *arg) {
        pthread_detach(pthread_self());

        thread_date *date = static_cast<thread_date *>(arg);
        // 接收请求
        char buffer[default_buffer_size];
        ssize_t n = recv(date->connect_fd, buffer, sizeof(buffer) - 1, 0);
        if(n <= 0) {   
            lg(WARNING, "read failed, %s", strerror(errno));

            return nullptr;
        } 
        buffer[n] = '\0';
        request req;
        lg(DEBUG, "反序列化：");
        req.deserializer(buffer);
        req.get_request_line();
        
        string response = req._version;
        
        string content = read_html_file(req.get_path());
        if(content.empty()) {
            response += " 404 NOT FOUND";
            content = read_html_file(err_file);
        }
        else {
            response += " 200 OK";
        }
        response += sep;
        response += "Content-Length: ";
        response += to_string(content.size());
        response += sep;
        response += "location: http://www.baidu.com/";
        response += sep;
        response += "content_type: ";
        auto it = date->server->_con_type.count(req._suffix);
        if(it == 0) {
            response += "text/html";
        }
        else {
            response += date->server->_con_type[req._suffix];
        }
        response += sep;
        response += "Set-Cookie: name=haha&&passwd=12345";

        response += sep;

        cout << "响应内容：" << response << endl;
        response += sep;
        response += content;
        n = send(date->connect_fd, response.c_str(), response.size(), 0);
        if(n <= 0) {
            lg(WARNING, "write failed, %s", strerror(errno));
            return nullptr;
        }
        close(date->connect_fd);
        delete date;
        return nullptr;
        
    }   


    void http_handler(thread_date *date) {
        // 获取客户端请求处理响应
        pthread_t td;
        pthread_create(&td, nullptr, thread_task, date);
    }

    void read_cont_type_config() {
        ifstream ifs("./content_type_conf");
        if(!ifs.is_open()) {
            lg(WARNING, "open content_type_conf failed, %s", strerror(errno));
            return;
        }
        string s;
        while(getline(ifs, s)) {
            size_t pos = s.find_first_of(':');
            if(pos == string::npos) {
                continue;
            }
            string key = s.substr(0, pos);
            string value = s.substr(pos + 1);
            _con_type[key] = value;
        }
        ifs.close();
    }

    void run() {
        // 初始化套接字
        _listen_socket.socket();
        _listen_socket.bind(_port);
        _listen_socket.listen();
        lg(INFO, "server is running on port %d", _port);

        read_cont_type_config();
        while (true) {
            // 接收客户端请求
            struct sockaddr_in client_addr;
            int connect_fd = _listen_socket.accept(&client_addr);
            thread_date *node = new thread_date(connect_fd, this);
            http_handler(node);
            
            // pthread_t wtd;
            // pthread_create(&wtd, nullptr, thread_write, &connect_fd);
            cout << "------------close(connect_fd)-------------------" << endl;
        }
    }

private:
    Socket _listen_socket;
    uint16_t _port;
    unordered_map<string, string> _con_type;
};